#pragma once

#include <iostream>
#include <memory>
#include <functional>
#include <unordered_map>
#include <cstring>
#include <sys/epoll.h>
#include "InetAddr.hpp"
#include "LogMessage.hpp"
#include "Socket.hpp"
#include "Epoller.hpp"

const int defaulteventsize = 128;

class TcpServer;
class Connect;

using func_t = std::function<void(Connect *)>;

class Connect
{
public:
    Connect(Socket *socket, TcpServer *Rector)
        : _socket(socket), _Rector(Rector)
    {
    }

    void Establishment(func_t reader, func_t writer, func_t errorer)
    {
        _reader = reader;
        _writer = writer;
        _excepter = errorer;
    }
    
    Socket* GetSock()
    {
        return _socket;
    }

    std::string& GetInBuffer()
    {
        return _inbuffer;
    }
    
    std::string& GetOutBuffer()
    {
        return _outbuffer;
    }

private:
    Socket *_socket;
    std::string _inbuffer;
    std::string _outbuffer;

public:
    // 回调函数
    func_t _reader;
    func_t _writer;
    func_t _excepter;

    // 回指指针
    TcpServer *_Rector;
};

class TcpServer
{
public:
    TcpServer(Socket *listensock, Epoller *epoll)
        : _listensock(listensock), _epoll(epoll), _eventsize(defaulteventsize), _events(new epoll_event[_eventsize]), _timeout(-1)
    {
    }

    ~TcpServer()
    {
        delete[] _events;
    }

    void LoopOnce()
    {
        _epoll->PrintAllSockfd();
        int n = _epoll->WaitEpoll(_events, _eventsize, _timeout);
        for (int i = 0; i < n; i++)
        {
            int sockfd = _events[i].data.fd;
            uint32_t event = _events[i].events;
            lg.LogMessage(Debug, "sockfd:%d  %sevent Ready", sockfd, EventToString(event).c_str());

            Connect *con = _maptables[sockfd];

            //将错误统一转化成读写，交给读写处理
            if (event & EPOLLERR)
                event |= EPOLLIN | EPOLLOUT;
                //con->_excepter(con);
            if (event & EPOLLIN)
                con->_reader(con);
            if (event & EPOLLOUT)
                con->_writer(con);
        }
    }

    void SetReadAndWrite(int sockfd, bool read, bool write)
    {
        int readevent = read == 1 ? EPOLLIN : 0;
        int writeevevnt = write == 1 ? EPOLLOUT : 0;

        uint32_t event = 0;
        event = readevent | writeevevnt | EPOLLET;
        _epoll->ModEpoll(sockfd, event);
    }

    void Start()
    {
        while (true)
        {
            LoopOnce();
        }
    }

    bool AddConnection(int sockfd, Connect *con)
    {
        if (sockfd < 0 || con == nullptr)
            return false;

        _maptables[sockfd] = con;
        return true;
    }

    void DelConnect(Connect* conn)
    {
        if (conn == nullptr)
        {
            lg.LogMessage(Warning, "conn is nullptr");
            return;
        }

        int sockfd = conn->GetSock()->GetSockfd();
        auto find = _maptables.find(sockfd);
        if (find == _maptables.end())
        {
            lg.LogMessage(Warning, "sockfd is not exists");
            return;
        }
        _maptables.erase(sockfd);
        _epoll->DelEpoll(sockfd);
    }

private:
    std::string EventToString(uint32_t event)
    {
        std::string str;
        if (event & EPOLLIN)
            str += "EPOLLIN ";
        if (event & EPOLLOUT)
            str += "EPOLLOUT ";
        if (event & EPOLLERR)
            str += "EPOLLERR ";
        if (event & EPOLLHUP)
            str += "EPOLLHUP ";

        return str;
    }

private:
    Socket *_listensock;
    Epoller *_epoll;
    std::unordered_map<int, Connect *> _maptables;
    size_t _eventsize;
    struct epoll_event *_events;
    int _timeout;
};